/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package iluscodelogica;

import excepciones.InterpreteException;
import java.io.Reader;
import java.io.StringReader;


public class IlusParser {
    
    private String mensaje;
    private IlusLexer ilusLexer;
    private Algoritmo algoritmo;

    /**
     * 
     */
    public IlusParser() {
    }

    public Token popToken(IlusLexer l) throws java.io.IOException {

        l.siguienteToken();
        Token tk = l.verToken();
        return tk;
    }

    public boolean esPuntoComa(Token t) {

        return (TipoToken.CHAR == t.getTipo() && ";".equals(t.getCadenaValor()));
    }

    

    public boolean esperarProgram(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException,InterpreteException {
        
        //Si la token es un program
        if (TipoToken.PROGRAMA == t.getTipo()) {
            //esperar procedimiento
            return esperarProcedimiento(a, popToken(l), l);
        } else {
            //Devuelve mensaje de error
            mensaje = "Se esperaba la palabra  Inicio del Programa pero no se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje);          
            
        }
    }

    public boolean esperarProcedimiento(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {
        
        //Si es  un procedimiento inserta el nombre del procedimiento y esperar sentencia
        if (TipoToken.PROCEDIMIENTO == t.getTipo()) {
            Token nombre = popToken(l);
            l.insertarSimbolo(nombre.getCadenaValor(), a.getTamanio());
            //Espera sentencia con punto y coma
            if (esperarSentencia(a, popToken(l), l) && esperarPuntoComa1(a, popToken(l), l)) {
                a.insertar(Instruccion.REGRESAR);
                return esperarProcedimiento(a, popToken(l), l);
            } else {
                return false;
            }
        } else {
            //Esperar el inicio de ejecución
            return esperarIniEjecutar(a, t, l);
        }
    }

    public boolean esperarIniEjecutar(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {

        if (TipoToken.INICIOEJECUCION == t.getTipo()) {
            
            a.setDirInicio(a.getTamanio());
            //Esperar la estructura principal de ejecución
            return esperarPrincipal(a, popToken(l), l);
        } else {
            mensaje = "Se esperaba Inicio de Ejecución pero no se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje);         
            
        }
    }

    public boolean esperarPrincipal(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {

        return (esperarListaSentencias(a, t, l)) && (esperarFinEjecutar(a, popToken(l), l));
    }

    public boolean esperarListaSentencias(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {
        if (esperarSentencia(a, t, l)) {
            //Token tk = popToken(l);
            if (esPuntoComa(popToken(l))) {
                return esperarListaSentencias(a, popToken(l), l);
            } else {
                l.sacarToken(l.verToken());
                return true;
            }
        } else {
            return false;
        }
    }


    public boolean esperarSentencia(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {
        
        //Si el tipo de token es inicio
        if (TipoToken.INICIO == t.getTipo()) {
            return (esperarListaSentencias(a, popToken(l), l) && esperarFin(popToken(l)));
            //Esperar la palabre repetir cantidad de veces e insertar instrucciónes
        } else if (TipoToken.REPETIR == t.getTipo()) {            
            a.insertar(Instruccion.REPETIR);
            if (esperarNumero(a, popToken(l), l)) {

                a.insertar(l.verToken().getNumeroValor());
                int saltoUbica = a.getTamanio();
                a.insertar();
                if (esperarVeces(popToken(l)) && esperarSentencia(a, popToken(l), l)) {
                    a.insertar(Instruccion.REGRESAR);
                    a.insertar(saltoUbica, a.getTamanio());
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
            //Esperar la palabra si para
        } else if (TipoToken.SI == t.getTipo()) {
            if (esperarPruebaLogica(a, popToken(l), l)) {
                a.insertar(Instruccion.SUBRAMA);
                int saltoUbica = a.getTamanio();
                a.insertar();
                if (esperarEntonces(popToken(l)) && esperarSentencia(a, popToken(l), l)) {
                    a.insertar(saltoUbica, a.getTamanio());
                    if (esSino(popToken(l))) {
                        a.insertar(Instruccion.RAMA);
                        int saltoUbica2 = a.getTamanio();
                        a.insertar();
                        a.insertar(saltoUbica, saltoUbica2 + 1);
                        if (esperarSentencia(a, popToken(l), l)) {
                            a.insertar(saltoUbica2, a.getTamanio());
                            return true;
                        } else {
                            return false;
                        }
                    } else {                        
                        l.sacarToken(l.verToken());
                        return true;
                    }
                } else {
                    return false;
                }
            } else {
                return false;
            }
            //Esperar la palabra mientras
        } else if (TipoToken.MIENTRAS == t.getTipo()) {
            int salto = a.getTamanio();
            if (esperarPruebaLogica(a, popToken(l), l)) {
                a.insertar();
                a.insertar();                
                if (esperarHacer(popToken(l)) && esperarSentencia(a, (popToken(l)), l)) {
                    a.insertar(salto + 1, Instruccion.SUBRAMA);
                    a.insertar(salto + 2, a.getTamanio() + 2);
                    a.insertar(Instruccion.RAMA);
                    a.insertar(salto);
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
            //Esperar nombre de procedimeinto
        } else if (TipoToken.NOMBRE == t.getTipo()) {
            Simbolo sym = l.buscarSimbolo(t.getCadenaValor());
            if (sym != null) {
                a.insertar(Instruccion.LLAMADA);                
                a.insertar(sym.getUbicacion());
                return true;
            } else {
                return false;
                   
                
            }
            //Esperar por una acción
        } else if (TipoToken.ACCION == t.getTipo()) {
            String cad = t.getCadenaValor();
            //Accion accion = Accion.buscarAccion(cad);
            a.insertar(Accion.buscarAccion(t.getCadenaValor()));
            //System.out.println("Inserto accion");
            return true;
            //Si no es ninguna palabra entonces palabra inseprada
        } else {
            mensaje = "Palabra inesperada " +t.getCadenaValor()+" se esperaba otra palabra";
            throw new InterpreteException(mensaje); 
            
        }
    }


    public boolean esperarFinEjecutar(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {

        if (TipoToken.FINPROGRAMA == t.getTipo()) {
            //Instruccion retorno = Instruccion.REGRESAR;
            a.insertar(Instruccion.REGRESAR);
            //Token tk = popToken(l);
            return esperarFinArchivo(a,popToken(l),l);//esperarFinalProg(a, popToken(l), l);
        } else {
            System.err.println("Se esperaba el fin del programa pero se encontro " + t.getCadenaValor());
            return false;
        }
    }

    public boolean esperarFin(Token t) throws InterpreteException {

        TipoToken fin = TipoToken.FIN;

        return esperar(fin, t);
    }


    public boolean esperarFinalProg(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException,InterpreteException {

        if (TipoToken.FINALPROGRAMA == t.getTipo()) {
            Token tk = popToken(l);
            return esperarFinArchivo(a, tk, l);
        } else {
            mensaje = "Se esperaba el final del programa pero se se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje); 
            
        }
    }


    public boolean esperarNombre(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {

        if (TipoToken.NOMBRE == t.getTipo()) {
            return esperarProcedimiento(a, popToken(l), l);
        } else {
            mensaje = "Se esperaba un nombre pero se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje); 
            
        }
    }


    public boolean esperarEntonces(Token t) throws InterpreteException {

        TipoToken entonces = TipoToken.ENTONCES;

        return esperar(entonces, t);
    }

    public boolean esSino(Token t) {

        return (TipoToken.SI_NO == t.getTipo());
    }

   
   
    public boolean esperarHacer(Token t) throws InterpreteException {

        TipoToken hacer = TipoToken.HACER;

        return esperar(hacer, t);
    }


    public boolean esperarPuntoComa(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {

        if (esperarSentencia(a, t, l)) {
            if (esPuntoComa(popToken(l))) {
                return esperarListaSentencias(a, popToken(l), l);
            } else {
                l.sacarToken(l.verToken());
                return true;
            }
        } else {
            return false;
        }
    }

 
    public boolean esperarVeces(Token t) throws InterpreteException{

        return esperar(TipoToken.VECES, t);
    }

 
    public boolean esperar(TipoToken esperado, Token tk) throws InterpreteException {

        if (esperado == tk.getTipo()) {
            return true;
        } else {
            mensaje = "Se esperaba "+ esperado + ", pero se encontro " +tk.getCadenaValor();
            throw new InterpreteException(mensaje);          
           
        }
    }

 
    public boolean esperar(String esperado, Token tk) throws InterpreteException {


        if ((TipoToken.CHAR == tk.getTipo()) && (esperado.equals(tk.getCadenaValor()))) {
            return true;
        } else {
            mensaje = "Se esperaba "+esperado + ", pero se encontro " +tk.getCadenaValor();
            throw new InterpreteException(mensaje); 
            
        }
    }

   
    public boolean esperarPruebaLogica(Algoritmo a, Token t, IlusLexer l) throws InterpreteException {

        if (TipoToken.DETECTA == t.getTipo()) {
            //Detecta accion = (Detecta) Accion.buscarAccion(t.getCadenaValor());
            a.insertar(Accion.buscarAccion(t.getCadenaValor()));
            //a.insertar(Accion.buscarAccion(t.getCadenaValor()));
            return true;
        } else {
            mensaje = "Se esperaba condición pero se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje); 
            
        }
    }

   
    public boolean esperarNumero(Algoritmo a, Token t, IlusLexer l) throws InterpreteException {

        if (TipoToken.NUMERO == t.getTipo()) {
            return true;
        } else {
            mensaje = "Se esperaba un número pero se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje); 
            
        }
    }

   
    public boolean esperarFinArchivo(Algoritmo a, Token t, IlusLexer l) throws InterpreteException {

        if (TipoToken.EOF == t.getTipo()) {

            return true;
        } else {
            mensaje = "Se esperaba fin del texto pero se encontro " +t.getCadenaValor();
            throw new InterpreteException(mensaje); 
            
        }
    }

   
    public boolean esperarPuntoComa1(Algoritmo a, Token t, IlusLexer l) throws java.io.IOException, InterpreteException {
        return esperar(";", t);
    }

   
    public Algoritmo parser(String archivo) throws java.io.IOException, InterpreteException {

        algoritmo = new Algoritmo();
        StringReader myStringReader = new StringReader(archivo);        
        Reader reader=myStringReader;
        ilusLexer = new IlusLexer(reader);
        if (esperarProgram(algoritmo, ilusLexer.verToken(), ilusLexer)) {
            return algoritmo;
        } else {
            return null;
        }
    }
}
